/***
 * @description
 * URL如：url/purl.html?a=1&b=b1#aa=v1&bb=v2
 * ?前面部分为：segment,后面部分为param
 * #后面部分为：fsegment
 *
 *
 * @namespace bs.url URL功能
 * @param url
 * @param strictMode
 * @returns {*}
 *
 * @example
 * URL如：http://localhost:8088/demo/url/purl.html?a=1&b=b1#aa=v1#bb=v2
 * var _url = bs.url();
 * query:a=1&b=b1
 * file:purl.html
 * directory:/demo/url/
 * path:/demo/url/purl.html
 * relative:/demo/url/purl.html?a=1&b=b1#aa=v1#bb=v2
 * source:http://localhost:8088/demo/url/purl.html?a=1&b=b1#aa=v1#bb=v2
 * protocol:http
 * authority:localhost:8088
 * host:localhost
 * port:8088
 *
 */
;bs.url = function(url, strictMode){
    var key = ['source', 'protocol', 'authority', 'userInfo', 'user', 'password', 'host', 'port', 'relative', 'path', 'directory', 'file', 'query', 'fragment'], // keys available to query
        aliases = { 'anchor': 'fragment' }, // aliases for backwards compatability
        parser = {
            strict: /^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/,  //less intuitive, more accurate to the specs
            loose: /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/ // more intuitive, fails on relative paths and deviates from specs
        },
        isint = /^[0-9]+$/;

    function parseUri(url, strictMode) {
        var str = decodeURI(url),
            res = parser[ strictMode || false ? 'strict' : 'loose' ].exec(str),
            uri = { attr: {}, param: {}, seg: {} },
            i = 14;
        while (i--) {
            uri.attr[ key[i] ] = res[i] || '';
        }
        // build query and fragment parameters
        uri.param['query'] = parseString(uri.attr['query']);
        uri.param['fragment'] = parseString(uri.attr['fragment']);
        // split path and fragement into segments
        uri.seg['path'] = uri.attr.path.replace(/^\/+|\/+$/g, '').split('/');
        uri.seg['fragment'] = uri.attr.fragment.replace(/^\/+|\/+$/g, '').split('/');
        // compile a 'base' domain attribute
        uri.attr['base'] = uri.attr.host ? (uri.attr.protocol ? uri.attr.protocol + '://' + uri.attr.host : uri.attr.host) + (uri.attr.port ? ':' + uri.attr.port : '') : '';
        return uri;
    }
    function promote(parent, key) {
        if (parent[key].length === 0) return parent[key] = {};
        var t = {};
        for (var i in parent[key]) t[i] = parent[key][i];
        parent[key] = t;
        return t;
    }

    function parse(parts, parent, key, val) {
        var part = parts.shift();
        if (!part) {
            if (isArray(parent[key])) {
                parent[key].push(val);
            } else if ('object' == typeof parent[key]) {
                parent[key] = val;
            } else if ('undefined' == typeof parent[key]) {
                parent[key] = val;
            } else {
                parent[key] = [parent[key], val];
            }
        } else {
            var obj = parent[key] = parent[key] || [];
            if (']' == part) {
                if (isArray(obj)) {
                    if ('' !== val) obj.push(val);
                } else if ('object' == typeof obj) {
                    obj[keys(obj).length] = val;
                } else {
                    obj = parent[key] = [parent[key], val];
                }
            } else if (~part.indexOf(']')) {
                part = part.substr(0, part.length - 1);
                if (!isint.test(part) && isArray(obj)) obj = promote(parent, key);
                parse(parts, obj, part, val);
                // key
            } else {
                if (!isint.test(part) && isArray(obj)) obj = promote(parent, key);
                parse(parts, obj, part, val);
            }
        }
    }

    function merge(parent, key, val) {
        if (~key.indexOf(']')) {
            var parts = key.split('[');
            parse(parts, parent, 'base', val);
        } else {
            if (!isint.test(key) && isArray(parent.base)) {
                var t = {};
                for (var k in parent.base) t[k] = parent.base[k];
                parent.base = t;
            }
            if (key !== '') {
                set(parent.base, key, val);
            }
        }
        return parent;
    }

    function parseString(str) {
        return reduce(String(str).split(/&|;/), function (ret, pair) {
            try {
                pair = decodeURIComponent(pair.replace(/\+/g, ' '));
            } catch (e) {
                // ignore
            }
            var eql = pair.indexOf('='),
                brace = lastBraceInKey(pair),
                key = pair.substr(0, brace || eql),
                val = pair.substr(brace || eql, pair.length);

            val = val.substr(val.indexOf('=') + 1, val.length);

            if (key === '') {
                key = pair;
                val = '';
            }

            return merge(ret, key, val);
        }, { base: {} }).base;
    }

    function set(obj, key, val) {
        var v = obj[key];
        if (typeof v === 'undefined') {
            obj[key] = val;
        } else if (isArray(v)) {
            v.push(val);
        } else {
            obj[key] = [v, val];
        }
    }

    function SET(obj, key, val) {
        var v = obj[key];
        if (typeof v === 'undefined') {
            obj[key] = val;
        } else if (isArray(v)) {
            v.push(val);
        } else {
            obj[key] = val;
        }
    }
    function lastBraceInKey(str) {
        var len = str.length,
            brace,
            c;
        for (var i = 0; i < len; ++i) {
            c = str[i];
            if (']' == c) brace = false;
            if ('[' == c) brace = true;
            if ('=' == c && !brace) return i;
        }
    }
    function reduce(obj, accumulator) {
        var i = 0,
            l = obj.length >> 0,
            curr = arguments[2];
        while (i < l) {
            if (i in obj) curr = accumulator.call(undefined, curr, obj[i], i, obj);
            ++i;
        }
        return curr;
    }

    function isArray(vArg) {
        return Object.prototype.toString.call(vArg) === "[object Array]";
    }

    function keys(obj) {
        var key_array = [];
        for (var prop in obj) {
            if (obj.hasOwnProperty(prop)) key_array.push(prop);
        }
        return key_array;
    }

    function __purl(url, strictMode){
        if (arguments.length === 1 && url === true) {
            strictMode = true;
            url = undefined;
        }
        strictMode = strictMode || false;
        url = url || window.location.toString();
        return {
            data: parseUri(url, strictMode),
            /**
             * URL属性变量获取
             * @param attr
             * @returns {*}
             */
            attr: function (attr) {
                attr = aliases[attr] || attr;
                return typeof attr !== 'undefined' ? this.data.attr[attr] : this.data.attr;
            },
            /**
             *参数获取或赋值
             * @param param
             * @param value
             * @returns {*}
             */
            param: function (param,value, isAppend) {
                if(typeof value !='undefined'){
                    if(typeof isAppend !='undefined' && isAppend){
                        set(this.data.param.query, param, value);
                    }else{
                        SET(this.data.param.query, param, value);
                    }

                }else{
                    return typeof param !== 'undefined' ? this.data.param.query[param] : this.data.param.query;
                }
            },
            /**
             *信息片断参数获取或赋值
             * @param param
             * @param value
             * @returns {*}
             */
            fparam: function (param,value, isAppend) {
                if (typeof value != 'undefined') {
                    if (typeof isAppend != 'undefined' && isAppend) {
                        set(this.data.param.fragment, param, value);
                    } else {
                        SET(this.data.param.fragment, param, value);
                    }

                }else{
                    return typeof param !== 'undefined' ? this.data.param.fragment[param] : this.data.param.fragment;
                }
            },
            /***
             * 返回指定层级路径名称
             * @param seg
             * @returns {*}
             *
             * @example
             * url:http://localhost:8080/aaa/bbb/?aa=1
             * bs.url().segment(1);//aaa
             */
            segment: function (seg) {
                if (typeof seg === 'undefined') {
                    return this.data.seg.path;
                } else {
                    seg = seg < 0 ? this.data.seg.path.length + seg : seg - 1; // negative segments count from the end
                    return this.data.seg.path[seg];
                }
            },

            /***
             * 返回路径的锚点名称[信息片断](即：#后面）
             * @param seg
             * @returns {*}
             *
             * @example
             * url:http://localhost:8080/aaa/bbb/?aa=1#mylabel
             * bs.url().fsegment(1);//mylabel
             */
            fsegment: function (seg) {
                if (typeof seg === 'undefined') {
                    return this.data.seg.fragment;
                } else {
                    seg = seg < 0 ? this.data.seg.fragment.length + seg : seg - 1; // negative segments count from the end
                    return this.data.seg.fragment[seg];
                }
            },
            toString:function(absolute){
               var _path = '';
               if(typeof absolute !='undefined' && absolute){
                   _path = this.data.attr['protocol'] + '://' + this.data.attr['authority'];
               }
               _path += this.data.attr['path'];

               //参数
               var _ppram = '';
                var _fparm = '';
               var _param =  this.data.param.query;
                var _fparam = this.data.param.fragment;
                for(var para in _param){
                    _ppram += para +'=' + _param[para];
                    _ppram += '&';
                }
                for (var para in _fparam) {
                    _fparm += para + '=' + _fparam[para];
                    _fparm += '&';
                }
                if(_ppram !=''){
                    _path += '?';
                    _path += _ppram.substr(0, _ppram.length-1);
                }
                if (_fparm != '') {
                    _path += '#';
                    _path += _fparm.substr(0, _fparm.length - 1);
                }
                return _path;
            }


        };
    };
    return __purl(url, strictMode);
};
